
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/printk.h>

#include <linux/hrtimer.h>

#include <linux/proc_fs.h>
#include <linux/seq_file.h>

static int test_intv = 1000;
module_param_named(intv, test_intv, int, 0444);

static int test_count = 120;
module_param_named(count, test_count, int, 0444);

struct ht_test {
    struct hrtimer tmr;
    
    ktime_t intv;
    int trig_count;
    
    struct proc_dir_entry *proc;
} test;

static int ht_test_show(struct seq_file *m, void *v)
{
    seq_printf(m, "HT, trig_count: %d\n", test.trig_count);

    return 0;
}

static int ht_test_open(struct inode *inode, struct file *file)
{
    return single_open(file, ht_test_show, NULL);
}

struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = ht_test_open,
    .read = seq_read,
    .llseek = seq_lseek,
    .release = single_release,
};

enum hrtimer_restart timerfd_tmrproc(struct hrtimer *htmr)
{
    test.trig_count++;
    //printk("HT, trig_count: %d\n", test.trig_count);
    
    if (test.trig_count < test_count)
    {
        hrtimer_forward_now(&test.tmr, test.intv);
        return  HRTIMER_RESTART;
    }
    else
    {
        return  HRTIMER_NORESTART;
    }
}

static int __init ht_init(void)
{
    test.intv = ms_to_ktime(test_intv);
    test.trig_count = 0;
    
    test.proc = proc_create("httest", 0444, NULL, &fops);

    hrtimer_init(&test.tmr, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
    hrtimer_set_expires(&test.tmr, test.intv);
    test.tmr.function = timerfd_tmrproc;
    
    hrtimer_start(&test.tmr, test.intv, HRTIMER_MODE_REL);

    return 0;
}

static void __exit ht_exit(void)
{
    hrtimer_cancel(&test.tmr);
    
    if (test.proc)
    {
        proc_remove(test.proc);
        test.proc = NULL;
    }

    return;
}

module_init(ht_init);
module_exit(ht_exit);

MODULE_LICENSE("GPL");

